/**
 *  Copyright (C) 2007 Marcos Diez < marcos AT unitron.com.br >
 *  Copyright (C) 2005 PGT-Linux.org http://www.pgt-linux.org
 *  Author: vandorpe Olivier <vandorpeo@pgt-linux.org>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <syslog.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdarg.h>

#include "usbb2k-api.h"
#include "fifoApi.h"
#include "usbb2k-main.h"
#include "cmdServer.h"
#include "yealinkProtocol.h"

int fifoWrite(yealinkDevice_t *yealinkDevice, const char *format, ...) {
	
	va_list ap;
	va_start(ap, format);

	char tempMsg[1000];
	int tempLength=vsnprintf(tempMsg, 1000, format, ap);
	int len=0;
	if (yealinkDevice->options.foregroundMode) {
		if (tempMsg[tempLength-1]=='\n') {
			tempMsg[tempLength-1]=0;
			syslog( LOG_INFO , "SEND: [%s]" , tempMsg);
			tempMsg[tempLength-1]='\n';
		} else {
			syslog( LOG_INFO , "SEND: [%s]" , tempMsg);
		}
	}
	if (yealinkDevice->socketFD) {
		len = write(yealinkDevice->socketFD, tempMsg, tempLength);
		if (len < 1) {
			syslog( LOG_ERR , "error writing to socket:[%s]", tempMsg);
		}
	}
	va_end( ap );
	return len;
}

/* CMD server thread */
// --- added by ant@loadtrax.com 30.10.05 ---
char ringing_tone[MSG_MAXL];

// Boucle sur la sonnerie tant que (ou):    // LOOP over the ringing as long as ( or )
// - annulation de la sonnerie (cmd RING 0) // cancel the ringtone
// - detection du decrochage du combiné.   // detects the swich over of the ???????
// - perte de la connexion client           // close to the client's conection
// - timeout = 0
static void *ring_th(void *arg) {
	int timeout, ringcycle, ringcyclelength;
	char thissegment;

	timeout=30;
	//syslog(LOG_INFO,"Ring Thread...on");
	ringing_state=ON;
	while (timeout>0 && ringing_state==ON) {
		timeout--;
		// --- added by ant@loadtrax.com 30/10/2005 ---
		// ringing_tone[] contains, eg, DbDt,  where letter
		// A/a=0.1s, Y/y=2.5s, UPPER=ring, lower=no ring
		// Z/z is 'stop here', ie, only do one ring loop
		// Try AaAaAx :-)
		ringcyclelength=strlen(ringing_tone);
		if (ringcyclelength<1) {
			// the user didn't set a valid tone, so apply a default. DbDt is
			// the UK ringing tone
			sprintf(ringing_tone, "DbDt");
			ringcyclelength=strlen(ringing_tone);
		}
		for (ringcycle=0; ringcycle<ringcyclelength; ringcycle++) {
			thissegment=ringing_tone[ringcycle];
			// --- added by chris at lunarflow.com 05/08/2008 ---
			if (thissegment=='@') {
				ringing_sendcid=ON;
				continue;
			}
			ringing_mode=(thissegment & 0x20) ? OFF : ON;
			if ((thissegment & 0x1f)==26) {
				// user specified 'z' or 'Z' so drop out here
				timeout=0;
				ringcycle=ringcyclelength;
				ringing_mode=OFF;
				ringing_state=OFF;
			} else
				usleep((thissegment & 0x1f)*100000);
		}
		// ---
	}
	//syslog(LOG_INFO,"Ring Thread..off");
	
	//this function is not mandatory to be called and valgrind says it leaks. so we don't call it
	//pthread_exit(NULL);
	return 0;
}

static int buffcmp(const char *buf1, const char *buf2) {

	int len1 = strlen(buf1);
	int len2 = strlen(buf2);

	if (len2 > len1)
		return -1;
	int i=0;
	while (i<len2) {
		if (buf1[i]!=buf2[i])
			return i+1;
		i++;
	}
	return 0;
}

// static int decode_cmd(const char *cmd_str, char *arg) {
static int string2token(const char *inputString, char *outputArg) {
	if (buffcmp(inputString, FIFO_READ_SWITH_TO_USB)==0) {
		return CMD_SWITCH_USB;
	}

	if (buffcmp(inputString, FIFO_READ_SWITH_TO_PSTN)==0) {
		return CMD_SWITCH_PSTN;
	}
	if (buffcmp(inputString, FIFO_READ_RING) ==0) {
		if (strlen(inputString)<5) {
			sprintf(outputArg, "0");
			return CMD_RING;
		}
		sprintf(outputArg, "%s", inputString+5);
		return CMD_RING;
	}
	// --- added by chris at lunarflow.com 05/08/2008 ---
	if (buffcmp(inputString, FIFO_READ_CID_AUDIO_ON)==0) {
		return CMD_CID_AUDIO_ON;
	}
	if (buffcmp(inputString, FIFO_READ_CID_AUDIO_OFF)==0) {
		return CMD_CID_AUDIO_OFF;
	}
	// --- added by ant@loadtrax.com 30/10/2005 ---
	if (buffcmp(inputString, FIFO_READ_TONE)==0) {
		sprintf(outputArg, "%s", inputString+5);
		return CMD_TONE;
	}
	// ---
	// added by simon dible 08-01-07
	if (buffcmp(inputString, FIFO_READ_DIALTONE_ON)==0) {
		return CMD_DIALTONE_ON;
	}
	if (buffcmp(inputString, FIFO_READ_DIALTONE_OFF)==0) {
		return CMD_DIALTONE_OFF;
	}
	//end add

	if (buffcmp(inputString, FIFO_READ_INFO)==0) {
		return CMD_INFO;
	}

	if (buffcmp(inputString, FIFO_READ_BIGTEST)==0) {
		return CMD_BIGTEST;
	}

	if (buffcmp(inputString, FIFO_READ_PICKUP_PSTN)==0) {
		return CMD_PICKUP_PSTN;
	}

	if (buffcmp(inputString, FIFO_READ_HANDUP_PSTN)==0) {
		return CMD_HANGUP_PSTN;
	}

	if (buffcmp(inputString, FIFO_READ_JOIN_USB_AND_PSTN)==0) {
		return CMD_JOIN_USB_AND_PSTN;
	}
	if (buffcmp(inputString, FIFO_READ_DETACH_USB_AND_PSTN)==0) {
		return CMD_DETACH_USB_AND_PSTN;
	}

	if (buffcmp(inputString, FIFO_READ_API_DEBUG)==0) {
		if (strlen(inputString) < 2) {
			sprintf(outputArg, "0");
			return CMD_API_DEBUG;
		}
		sprintf(outputArg, "%s", inputString+2);
		return CMD_API_DEBUG;
	}

	if (buffcmp(inputString, "API_DEBUG")==0) {
		if (strlen(inputString) < 10) {
			sprintf(outputArg, "0");
			return CMD_API_DEBUG;
		}
		sprintf(outputArg, "%s", inputString+10);
		return CMD_API_DEBUG;
	}

	return CMD_NA;
}

void *cmdServer(yealinkDevice_t *yealinkDevice) {
	int ring_type;
	int ret;

	char msg[MSG_MAXL+1], arg_cmd[MSG_MAXL+1];
	pthread_t ring_thread;

	syslog(LOG_INFO,"CMD server started");

	do {
		// msgl=read(client, msg, MSG_MAXL -1);
		// msgl=getCommand(yealinkDevice->sock, msg, MSG_MAXL -1);
		if (fgets(msg, MSG_MAXL-2, yealinkDevice->socketFile) ) {

			// process CMD

			if (yealinkDevice->options.foregroundMode) {
				int lenght = strlen(msg) -1;
				if (msg[lenght] == '\n') {
					msg[lenght]=0;
					syslog(LOG_INFO,"RECV: [%s]",msg);
					msg[lenght]='\n';
				} else {
					syslog(LOG_INFO,"RECV: (no \\n in the end)[%s] ",msg);
					continue;
					// that must be crap;
				}

			}
			int token = string2token(msg, arg_cmd);

			// processToken(token, arg_cmd );

			switch (token) {
			case CMD_SWITCH_USB:
				usbb2k_switch_mode(yealinkDevice->dev_h, USB_MODE);
				break;
			case CMD_SWITCH_PSTN:
				usbb2k_switch_mode(yealinkDevice->dev_h, PSTN_MODE);
				break;
			case CMD_RING:
				ring_type=atoi(arg_cmd);
				if (ring_type>0 && ringing_state==OFF && handset_state==OFF) {
					//syslog(LOG_INFO,"Ring init_thread");
					if ((ret=pthread_create(&ring_thread, NULL, ring_th, NULL))
							!=0) {
						pthread_detach(ring_thread);
						syslog(LOG_ERR,"ring thread error");
						closelog();
					}
				} else {
					ringing_state=OFF;
					ringing_mode=OFF;
				}
				break;
			// --- added by chris at lunarflow.com 05/08/2008 ---
			case CMD_CID_AUDIO_ON:
				usbb2k_cid_audio(yealinkDevice->dev_h, ON);
				break;
			case CMD_CID_AUDIO_OFF:
				usbb2k_cid_audio(yealinkDevice->dev_h, OFF);
				break;
				// --- added by ant@loadtrax.com 30/10/2005 ---
			case CMD_TONE:
				// copy arg_cmd[] into ringing_tone[]
				sprintf(ringing_tone, "%s", arg_cmd);

				break;
				// ---

				//added by simon dible 08-01-07
				//turn the dial tone on or off based on user command
			case CMD_DIALTONE_ON:
				dialtone = ON;
				break;
			case CMD_DIALTONE_OFF:
				dialtone = OFF;
				break;
				//end add
			case CMD_INFO: {
				printInfo(msg, MSG_MAXL, yealinkDevice);
				fifoWrite(yealinkDevice, "%s", msg);
			}
				break;

			case CMD_API_DEBUG:
				api_debug(yealinkDevice->dev_h, atoi(arg_cmd) );
				break;

			case CMD_BIGTEST:
				bigtest(yealinkDevice->dev_h);
				break;

			case CMD_PICKUP_PSTN:
				pickup_pstn(yealinkDevice->dev_h);
				break;

			case CMD_HANGUP_PSTN:
				hangup_pstn(yealinkDevice->dev_h);
				break;

			case CMD_JOIN_USB_AND_PSTN:
				b3g_join_usb_and_pstn(yealinkDevice->dev_h);
				break;

			case CMD_DETACH_USB_AND_PSTN:
				b3g_detach_usb_and_pstn(yealinkDevice->dev_h);
				break;

			case CMD_NA:

			default:
				syslog(LOG_INFO,"undefined command [%s]", msg);
			}
		} else { // the file was closed by the client
			//printf("cmd server off\n");
			cmdServer_loop=OFF;
		}

	} while (cmdServer_loop);

	syslog(LOG_INFO,"CMD server shutdown");
	return 0;
}

